Release 1.2.0: federation consumers + project_status binding probe#3
Release 1.2.0: federation consumers + project_status binding probe#3tachyon-beep wants to merge 37 commits into
Conversation
Package fully renamed charter->plainweave. Updates live member-facing refs: the federation value-add audit (incl. plainweave_* tool names), consumer tickets, vision authority split, federation.md, README/index, the advisory-not-gating concept, PRD-0001, metrics guardrail, and the sibling-import guard (FORBIDDEN_IMPORT_ROOTS) + consumer-ticket test. Leaves generic 'on-charter', the draft-charter skill, historical plan docs, and the evidence-entangled sibling guards (member-diff path + source-grounding) untouched -- those need a baseline refresh / re-grounding against the real plainweave repo, tracked separately. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…cessor Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Revert the always-on top-version verification guard added to _run_migrations (and its _top_version_objects_present helper + _TOP_VERSION_OBJECTS_VERSION constant) as out-of-scope migration-runner hardening. The in-scope v4 block in _schema_presence_floor is retained. Rewrite test_presence_floor_recovers_dropped_table to exercise the real user_version==0 reconcile path (meta claims v4, table dropped, v2/v3 intact -> floor to v3, re-run v4), mirroring the established v3-style test. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…unverified/unavailable)
…ndetermined branch Fix module docstring purity claim to list all three imports (collections.abc + typing + warpline.listing.reason).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… code Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extends test_stale_first_is_secondary_to_an_explicit_sort with a third entity Z (depth=0, stale) alongside X (depth=0, fresh) and Y (depth=1, stale). The new assertion (c) verifies that within the same depth bucket the stale item precedes the fresh one — the gap left unexercised by the previous two-item layout. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… pre-verify envelope
Also relax dogfood real-member parity check from strict path equality to subset check (warpline_paths ⊆ baseline_paths): warpline only tracks code entities, not Makefile/README/docs, so the strict == was a false failure when the selected lacuna commit happened to touch non-entity files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…+ error symmetry)
Fix 1 (latent crash): list_change_events_for_key_ids built IN-clause
placeholders from raw key_ids but bound sorted(set(key_ids)), raising
sqlite3.ProgrammingError on any duplicate key_id. Derive both from
unique_ids = sorted(set(key_ids)). Regression test added (RED→GREEN).
Fix 2 (doc): _schema_presence_floor docstring referenced v(N>3); after
the v4 migration it should read v(N>4).
Fix 3 (truth-table): lock the precedence that a mixed covers() result on
the latest change (one event None, another True) yields fresh — a positive
cover wins once any True exists.
Fix 4 (error symmetry): empty/blank commit in verify_record now raises
MissingRequiredFieldError(rejected_field="commit") rather than flowing
through resolve_commit("") → BadRevisionError. Present-but-unresolvable
refs still raise BadRevisionError. Test added (RED→GREEN); existing
bad-ref test confirmed GREEN.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… order Replace _latest_covering_event (picks by verified_at recency) with _tightest_covering_event (picks the covering event minimising commits_between to latest_change). When gate records arrive out of git-ancestry order the old helper overstated commits_behind; the new one always reflects the most-advanced proof on record. State classification, reason triples, and fresh/unverified/unavailable paths are unchanged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…apture The `client is None` branch of `capture_edge_snapshot` unconditionally UPSERTed completeness=SKIPPED and DELETEd edges for the (repo, commit, loomweave) key across two non-atomic commits. For a commit that already held a FULL/DELTA row this downgraded a real edge graph to a 0-edge SKIPPED row — the R3 data-loss class the atomic capture path prevents, but which the absent-client case never reached (warpline-d7d04243b2). Fix (the ticket's preferred, enrich-only/fail-closed option): - store: add get_edge_snapshot(repo_id, commit_sha, source) — exact-key lookup (latest_snapshot is repo-latest-by-id, the wrong key). - snapshot: when a prior FULL/DELTA exists, preserve it untouched and return recapture_skipped=True against it (regardless of staleness — a stale FULL is still a real graph; the read path downgrades stale completeness itself). With no usable prior, write SKIPPED via the single-transaction capture_snapshot_atomic(edges=[]), retiring the old two-commit UPSERT+DELETE write. - commands: append a PRESERVED warning. The envelope is then the honest triple — completeness=FULL/DELTA (real graph) + sei=unavailable (peer down) + warning (not refreshed) — mirroring the if_stale_after short-circuit (edges=0, entities=0, already_current). test_gv_lw_3 realized "loomweave absent -> SKIPPED" as a recapture at the SAME commit holding the FULL, i.e. it asserted the downgrade — contradicting GV-LW-6 (preserve prior, never a degraded/0-edge row). The frozen manifest assert text never said "absent at a commit holding a FULL", so it stays literally true; only the test's incidental same-commit realization moves to a no-prior commit (c2). Manifest JSON untouched; the vector set stays frozen at 19. The preserve invariant is locked by GV-LW-6 plus new unit/CLI tests (full-preserve, delta-preserve, no-prior-atomic, and a CLI PRESERVED-warning test); the old test that pinned the bug is inverted, and present-client edge-replace stays covered by test_capture_snapshot_atomic_replaces_edges. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… + ENVELOPE_KEYS build_envelope always emits the top-level enrichment_reasons key (envelope.py:97), but the static contract fixtures and ENVELOPE_KEYS omitted it, and _assert_frozen_envelope's subset check (ENVELOPE_KEYS <= set(fixture)) silently tolerated the absence — so the static reference envelopes no longer matched the runtime the hub consumes. - Add enrichment_reasons to ENVELOPE_KEYS. - Add the faithful runtime block to both mcp-response fixtures: the reserved-but-honest requirements 'disabled' triple on both, plus the sei 'clean' triple on changed (its enrichment.sei is 'present', and change_list attaches sei_reason); reverify carries requirements only (it passes no enrichment_reasons to build_envelope). - Assert the block in _assert_frozen_envelope, mirroring build_envelope's contract (envelope.py:78-88) + reason() (listing.py:43): every dimension in the closed vocab, every value a canonical reason_class, non-clean carries cause+fix, and requirements is universally 'disabled'. Test-only hygiene; no src change. Closes warpline-fc09bdeddd. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ueError)
listing.reason() enforced its carrier rule (class-membership, and every
non-clean carrier MUST carry both cause and fix) with bare asserts, which
python -O strips. build_envelope only re-checked reason_class membership,
not cause/fix. So under -O a hollow {reason_class: 'disabled'} triple with
no cause/fix would pass validation and ship -- the exact 'unexplained
absence' the honesty doctrine forbids.
- listing.py: promote both reason() asserts to raised ValueError (keep the
clean short-circuit). Closes the helper-built-triple hole.
- envelope.py: build_envelope now rejects a non-clean enrichment_reasons
triple missing cause/fix (clean exempt). Closes the parallel
hand-built-via-kwarg hole, which bypassed reason() even without -O.
- _enrichment.py: sei_reason() is non-Optional -- raises ValueError on an
out-of-vocab state instead of returning None.
- commands.py: delete the four now-dead 'assert sei_triple is not None'
narrowing guards (also -O-strippable); sei_reason's non-Optional return
makes them redundant. Param kept as str (not Literal) to avoid mypy
[arg-type] churn at the call sites.
Tests: flip the reason() AssertionError expectations to ValueError; rewrite
the sei_reason None test to expect ValueError; add coverage that
build_envelope rejects a hollow non-clean triple via the enrichment_reasons=
kwarg.
Internal hardening; frozen warpline.<contract>.v1 envelope and the closed
enrichment vocab unchanged. Verified: full suite green (5 known pre-existing
env failures only), python -O proof passes on all paths, mypy unchanged
(1 pre-existing no-any-return), ruff clean. Closes warpline-d88e223731.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…→Plainweave refs Adds/normalizes the 'not-for-X' Banner naming this member's specific misuse (deconfliction-first, not security/compliance); fixes hardcoded Charter→Plainweave prose. Re-vendored kit; build green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…econcile workspace to plan/verification-freshness Execution-only session on the PDR-0006 deferred follow-ups; no product bet decided/killed/reprioritized, so no new PDR. current-state.md reconciled from the stale 2026-06-24 (main @ v1.2.0) brief to present reality: branch plan/verification-freshness, verification-freshness BUILT-but-unreleased (Track B in CHANGELOG [Unreleased]; reconciliation-debt flag for its missing acceptance PDR), and the follow-up tracker state. metrics.md gains a 2026-06-26 quality-debt-burndown reading (honesty guardrail strengthened: weft-reason invariant survives python -O); no reversal trigger crossed. roadmap.md/vision.md untouched (no horizon or strategy change). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…worklist.v1
Add an honest completeness+staleness self-assessment to
warpline.reverify_worklist.v1 so wardline (and downstream consumers) can tell a
narrowed/partial impact scope from an authoritative one, instead of treating any
worklist as proven-good.
- New additive data.impact_completeness OBJECT carrying BOTH axes in one place:
staleness (as_of producer timestamp + graph_fresh + graph_ref) and completeness
(status: complete|partial|unknown + depth_capped + unresolved_count + reasons[]).
status="complete" ONLY when the impact set is genuinely exhaustive (positively
fresh FULL graph, no depth cap, zero unresolved); any gap -> partial; no graph
-> unknown. Never complete on a guess. wardline mirrors this object verbatim
into producer_completeness.
- The FROZEN raw-snapshot data.completeness STRING enum is unchanged (raw signal
vs. derived assessment); additive on .v1, no v2 bump.
- New depth_capped signal in blast_radius: a node at the depth horizon with an
out-edge to an unseen target means the traversal truncated reachable impact.
- Publish the drift-checkable contract at contracts/reverify_worklist.v1.schema.json
(JSON Schema draft 2020-12); validates real worklist output and rejects malformed
payloads (bad status/reason, completeness-as-object, missing entity.{locator,sei},
malformed as_of via an RFC3339 pattern since format is annotation-only).
- Consumer-side risk-as-verification gate (completeness_risk): an absent or
non-complete assessment degrades to risk=unavailable with an explicit reason
(completeness_not_declared / completeness_partial); never clean.
Coordination with wardline (deviations from the original D1 prompt, both from
mid-flight reconciliation): the field is data.impact_completeness (data.completeness
was already frozen as a string); data.generated_at is NOT emitted (it never existed)
- the producer timestamp is folded into impact_completeness.as_of so one object
declares completeness AND staleness.
End-to-end verified through the real MCP handler and CLI surfaces (not just the
internal command), confirming no response projection strips the field. jsonschema
added as a dev-only dep (runtime deps stay empty).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tion) Close the verification_source_absent gap D1 left open: warpline can now read a change as proven-good, sourced from wardline's attestation rather than minting a clean verdict of its own. - New pure consumer _attest.worklist_risk: layered on D1's completeness gate, a COMPLETE worklist whose every affected entity is attested clean at its CURRENT body reads risk="proven" (reason_code attested_clean). Mechanical (commit, content_hash) equality per SEI against the wardline-attest-2 boundaries (verdict=="clean", not dirty, commit pins). All-or-nothing. - Echo of wardline's authority, NOT a warpline clean: the HMAC signature is not verified by warpline (it does not hold the shared key), so every proven verdict carries authority="wardline" + signature_verified=false + the mechanical basis. - Honesty edges all degrade to unavailable with an explicit machine reason: no bundle (verification_source_absent), non-attest-2 schema, dirty tree, null / mismatched commit, sei_source unavailable, null sei/content_hash, non-clean verdict, any unmatched affected entity (attestation_incomplete). - loomweave.resolve_content_hash_for_locator: the entity-body content_hash from the SAME entity_resolve round trip warpline already makes for the SEI. Verified byte-identical to wardline's bundle value across loomweave's MCP entity_resolve and HTTP /api/v1/identity/sei surfaces (the empirical make-or-break: without hash parity the seam would be a dead match-nothing path). Verified end-to-end against live loomweave: a complete worklist + a matching attest-2 bundle reads proven-good; a content_hash drift degrades to attestation_incomplete. Scope note: the consumer logic + loomweave hash retrieval are implemented and tested. A CLI/MCP entrypoint that ingests a pushed --attest-bundle and a worklist-level wiring are the next increment. Env caveats: the installed wardline CLI emits wardline-attest-1 (the consumer correctly rejects non-attest-2), and warpline's own boundary-free source cannot itself produce a populated bundle. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Close the verification_source_absent gap in ACTUAL OPERATION, not just as a pure function: warpline reverify now ingests a pushed wardline-attest-2 bundle and emits its risk-as-verification posture on every worklist. - `warpline reverify --attest-bundle <file>` (CLI) and the `attest_bundle` MCP arg (advertised + consumed) read the PUSHED, UNTRUSTED bundle JSON and pass it to reverify_worklist. - data.risk_verification is now emitted on EVERY worklist (additive .v1): with no bundle a complete worklist honestly reads verification_source_absent; with a matching bundle whose every affected entity is attested clean at its current body it reads proven-good (echo of wardline, HMAC unverified). - The per-SEI current content_hash is sourced from the SAME loomweave entity_resolve round trip warpline already makes for the SEI (_attest_content_hashes; fail-soft + read-only — an unreachable loomweave yields no hashes, so entities are honestly unmatched, never faked-good). Only paid when a bundle is supplied. - The verdict describes the FULL filtered+sorted worklist (pre-page, like verification_summary), deduped/order-preserving across affected SEIs. - contracts/reverify_worklist.v1.schema.json documents risk_verification (optional/lenient — wardline's vendored fixtures needn't carry warpline's consumer-side posture). Contract fixture + golden envelope updated. Verified through the real CLI binary and the MCP handler: a pushed --attest-bundle moves a complete worklist's verdict off verification_source_absent (consumed), and the live demo confirms proven-good for an entity loomweave can resolve. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…he page The risk-as-verification content_hash lookup built its sei->locator map from the post-apply_page `items`, but affected_seis (and the all-or-nothing proven check) spans the FULL pre-page set. So any worklist larger than `limit` (default 100), or trimmed by overflow, could NEVER read proven-good even when the bundle attested every affected entity — the off-page entities had no locator, hence no content_hash, hence attestation_incomplete. Capture the sei->locator map at the same pre-page point as affected_seis and pass it to _attest_content_hashes. New regression test drives proven through the full reverify_worklist + build_envelope path with TWO entities at limit=1 (one off page 1) and asserts matched==affected==2 — which also confirms the wardline-clean carrier riding inside data.risk_verification leaves the honesty invariant intact. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…erify Lights up the previously-inert `legis` federation member. `reverify --include_federation` now consults legis's authoritative governance_read.v1 (verified clearances only — operator override / cleared sign-off) via a real LegisGovernanceClient over `legis governance-read <SEI> --json`, mirroring the WardlineDossierClient CLI idiom. The contract is mirrored as the source of truth at contracts/governance_read.v1.schema.json (legis OWNS it). Trust boundary held: warpline ECHOES governance advisorily and never gates (GV-LG-1 — no governance_verdict in output; an adversarial test pins that the clearance does NOT move verification_summary / risk_verification / impact_completeness / item order). The clearance content_hash is echoed verbatim, NOT re-derived against the current body — governance is an echo, not a verdict (contrast the attest-2 risk-as-verification path). Honesty: an empty read is `governance: absent` = "no verified clearance," which deliberately conflates ungoverned, unknown-SEI, and an entity actively BLOCKED awaiting sign-off — so `absent` is never "ungoverned" (disclosed in the schema description + the federation reference docs). Wiring is CAPABILITY-GATED: the client is wired only when the installed legis advertises `governance-read`, so an unshipped read surface stays honestly `disabled` (capability absent) rather than a forced `unreachable`, and lights up automatically once legis ships the verb. The stale disabled `fix`/blocker text (which recruited "wire a LegisClient" — now done) is updated to recruit installing/upgrading legis. The governance_read.v1 schema vectors are the contract's canonical samples, not a live capture: at time of writing the installed legis exposes no governance-read verb yet. 477 passed, 1 skipped; ruff + mypy clean on changed files. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…reverify The handler's True-branch (available()=True -> records -> governance: present) was only exercised through commands.reverify_worklist; this drives it end-to-end through the MCP handler so the 3-line capability-gated construction has direct coverage. filigree/wardline degrade independently here — governance is unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…iminated union) legis tightened the contract since the first hand-off: the schema now enforces the status<->shape discriminated union via allOf — status 'unavailable' REQUIRES a non-empty `unavailable` reasons array and empty `records` (maxItems:0); status 'checked' MUST NOT carry the `unavailable` key. Plus minItems:1 on reasons and minLength:1 on each reason string. Backward-compatible (legis never emitted the now-rejected shapes), so warpline's consumer built against the looser draft is not broken. Re-mirrored BYTE-FOR-BYTE from legis's canonical (diff -q == identical) so the mirror stays a true mirror with no drift. Added consumer-side rejection tests pinning every arm of the union (checked-carrying-unavailable, unavailable-missing- reasons, empty reasons, blank reason string, unavailable-carrying-records) so warpline validates with exactly the tightness legis emits — an 'unavailable' can never masquerade as a clean empty 'checked'. 483 passed, 1 skipped; ruff + mypy clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d CLI (drop --json) Verified warpline against legis's now-shipped adapters (legis commits a98d147 CLI / 4464c18 HTTP / 031606f MCP). One interface drift found and fixed: legis's CLI is `legis governance-read <SEI>` with output ALWAYS JSON — there is no `--json` flag (legis src/legis/cli.py:117-125). warpline was passing `--json` (carried over from the first hand-off, which legis later dropped), which is an argparse error -> nonzero exit -> the read would have degraded to `unreachable` forever, even once legis is installed. Removed the flag; pinned the exact argv (`["legis","governance-read",<sei>]`, no `--json`) with a regression test so the invocation form can't drift back out of sync. Everything else is in sync: - schema: warpline's mirror is BYTE-IDENTICAL to legis's canonical (diff -q). - shape: legis's REAL conformance golden envelope validates against warpline's mirror AND round-trips through the consumer parse path. Vendored that golden vector (legis-governance-read.golden.json) as a durable interface-agreement guard that fails loud if the emitted/consumed shapes ever diverge. Still capability-gated: the INSTALLED legis binary is behind source (no governance-read verb yet), so warpline stays honestly `disabled` until it lands, then auto-wires. 485 passed, 1 skipped; ruff + mypy clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add `warpline_project_status_get` / `project_status` (`warpline.project_status.v1`), a read-only store-binding/health probe for the Weft federation's Lacuna MCP-attachment harness. It reports whether THIS build can read and serve the snapshot store for a given repo (`data.binding_ok`), reading `schema_version` FROM INSIDE the store — never mere directory existence — so a stale-but-running warpline that cannot read its `.weft/warpline` store at a compatible schema is caught (the loomweave-incident failure class). It is the first GENUINELY read-only tool: `writes_local_state=false`, `mutates_paths=[]`. It creates/migrates no snapshot state (an absent store reports absent — `store_absent` + a capture next-action — without creating a DB), leaves `warpline.db` byte-for-byte unchanged, and never reaches `WarplineStore.open`'s mkdir/migrate path. A corrupt store is `store_unreadable`; a store written by a newer build is `schema_ahead` (all not-bound cases: `binding_ok=false`, `schema_version=null`). The store is read strictly read-only via `mode=ro` (chosen over `immutable=1` so the probe always reads the latest committed schema version; opening a present WAL store may spawn gitignored `-wal`/`-shm` coordination sidecars, which are not snapshot state). - store.py: `read_store_binding` read-only reader, `StoreBinding`, shared `store_repo_id`, closed `STORE_STATUS_VOCAB`. - commands.py: `project_status` command (CORE envelope; binding facts under `data`), fail-closed vocab guard. - mcp.py: tool spec + handler + honesty-invariant registrations (`_HANDLER_CONSUMES` / `_KNOWN_FASTFOLLOW_DEAD`); `assert_inputschema_consumed` stays green. - mcp_smoke.py: structural binding-probe check. - Generalized the live tools/list metadata assertion to honestly allow a read-only tool (mutates_paths==[]); declared the read-only probe's intentional exclusion from the frozen six-tool federation-data-contract inventory. Tests-first (TDD) + an adversarial multi-lens review (zero contract violations); 21 tests in tests/test_project_status.py pin the three required cases, both arms of the schema-ahead `max(meta, user_version)` floor, empty-but-serveable and below-HIGHEST serveability, durable-DB immutability under probing, and repo-scoped counts. Gate: 506 passed / mypy --strict clean / ruff clean / `wardline scan --fail-on ERROR` exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cut the 1.2.0 release line: D1 impact-completeness, the Rung-2 wardline-attest-2 and legis governance_read.v1 federation consumers, and the read-only project_status store-binding probe. Gate green on the merge result. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: def6d43cfc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| for it in items: | ||
| entity = it.get("entity", {}) | ||
| sei = entity.get("sei") | ||
| if isinstance(sei, str) and sei and sei not in _seen_seis: |
There was a problem hiding this comment.
Treat unkeyed worklist entities as unattested
When a complete worklist contains an affected item whose entity.sei is null, this condition simply skips it, so affected_seis can be empty and worklist_risk() will return risk: "proven" for an empty attest bundle (matched: 0, affected: 0). In repos where SEI resolution was unavailable or an entity has only a locator, that falsely marks a real affected entity as attested instead of degrading to attestation_incomplete; unkeyed affected entities need to be counted as unmatched/blocking for the all-or-nothing verdict.
Useful? React with 👍 / 👎.
| affected_seis: list[str] = [] | ||
| affected_sei_locators: dict[str, str] = {} | ||
| _seen_seis: set[str] = set() | ||
| for it in items: |
There was a problem hiding this comment.
Build attestation scope before list filters
This loop derives the all-or-nothing attestation set from items after apply_filters() has already narrowed the list, so a caller can pass a filter such as path_prefix that hides an unattested affected entity and still get risk: "proven" for the remaining subset. Because risk_verification is reported as the change/worklist-level verdict rather than a filtered-view verdict, its scope should be based on the full pre-filter affected set (as already done for pagination) so filters cannot turn a partial attestation into a proven one.
Useful? React with 👍 / 👎.
| FROM change_events ce | ||
| WHERE ce.repo_id = ? | ||
| AND ce.entity_key_id IN ({placeholders}) | ||
| ORDER BY COALESCE(datetime(ce.changed_at), ce.changed_at), ce.id |
There was a problem hiding this comment.
Order verification changes by commit topology
For entities with multiple changes whose author timestamps are not topologically ordered (common after rebases/amends or hand-set GIT_AUTHOR_DATE), this changed_at ordering can put a descendant change before its parent, while compose_verification_freshness() treats entity_change_commits[-1] as the latest change. In that case a verification recorded at the older commit is reported fresh even though a later descendant change exists; the freshness path needs to determine latest by commit ancestry/commit order rather than author-time ordering.
Useful? React with 👍 / 👎.
| return { | ||
| "schema": bundle.get("schema"), | ||
| "commit": payload.get("commit"), | ||
| "dirty": bool(payload.get("dirty")), |
There was a problem hiding this comment.
Fail closed when attestation dirty flag is absent
When a supplied wardline-attest-2 payload omits dirty or sets it to null, bool(payload.get("dirty")) normalizes that unknown state to False, so a bundle with no clean-tree pinning signal can pass the bundle-level checks and return risk: "proven" if the boundaries match. Since the dirty flag is the guard that makes the attested commit truthfully pin the source, absence of that field should degrade instead of being treated as clean.
Useful? React with 👍 / 👎.
| "required": ["risk", "reason_code", "reason"], | ||
| "properties": { | ||
| "risk": { "enum": ["proven", "unavailable"] }, | ||
| "reason_code": { "type": "string" }, |
There was a problem hiding this comment.
Constrain risk verification reason codes in schema
The contract description names a closed machine-code vocabulary for risk_verification.reason_code, but the schema accepts any string here, so a typo or producer drift in this new federation field still validates and can reach consumers that switch on those codes. Please encode the same enum used by the producer/tests in the schema so the published contract catches invalid risk-verification states.
Useful? React with 👍 / 👎.
| "additionalProperties": true, | ||
| "required": [ | ||
| "completeness", | ||
| "impact_completeness", |
There was a problem hiding this comment.
Keep new completeness field optional in v1 schema
Adding impact_completeness to the required list makes the published warpline.reverify_worklist.v1 schema reject pre-D1 v1 worklists, even though the schema description says v1 changes are additive and the consumer path explicitly degrades a missing field to completeness_not_declared. If wardline or another consumer validates pushed v1 payloads before applying that degradation logic, old-but-valid producers now fail validation instead of being handled honestly; leave this field optional in v1 or bump the schema version for the breaking requirement.
Useful? React with 👍 / 👎.
| records = payload.get("records", []) | ||
| if not isinstance(records, list): | ||
| return [] |
There was a problem hiding this comment.
Reject malformed governance-read records
When legis returns status: "checked" but omits records or returns a non-array, this defaults to [] and the federation consult reports a clean, earned-empty governance result. Because governance_read.v1 requires records to be an array, a malformed or truncated response should become LegisGovernanceUnavailable/unreachable; otherwise transport or producer drift is silently converted into governance=absent.
Useful? React with 👍 / 👎.
…rch-analysis reports Source/test fixes (PDR-0023 honesty + list-ergonomics lineage): - _attest.py: fail closed on an EMPTY/unkeyed affected set — a complete worklist with no SEI-keyed entity (e.g. an install/backfill run while loomweave was unavailable) is `attestation_unkeyed`, never a vacuous proven verdict on the satisfied-because-empty all-or-nothing loop. - commands.py (change_list): rebuild the reverify/blast follow-up SEEDS (data.changed_refs + next_action arguments) from the FILTERED set, so a path_prefix/actor/since filter narrows the advertised next-action scope too — not just the visible items. - commands.py (capture_snapshot): idempotency reflects whether THIS capture was skipped/reused (`recapture_skipped`), not whether ANY prior snapshot existed — a new row for a different commit is `created`, only the loomweave-absent preserve path is honestly `already_current`. - listing.py (apply_page): reject a non-positive `limit` loudly (else an empty window with a self-referential next_cursor loops a cursor-following client) — honor the advertised knob or reject it, like every other numeric bound. - tests: cover each of the above (test_attest, test_honesty_invariant, test_list_ergonomics) + a lint cleanup in test_snapshots. Docs: architecture-analysis reports (00–06) under docs/arch-analysis-2026-06-28-0728/. The `temp/` scratch subdir is intentionally left out of version control (it carries developer absolute paths the test_public_docs_hygiene gate forbids). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The scratch subdir from the 2026-06-28 architecture analysis: the technical-debt catalog and the two validation catalogs. The lone developer absolute path in debt-catalog.md (`/home/john/warpline/src/warpline/`) is rewritten repo-relative (`src/warpline/`) so the test_public_docs_hygiene gate stays green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5ef2472f48
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| exhaustive = ( | ||
| completeness == "FULL" | ||
| and graph_fresh | ||
| and not depth_capped | ||
| and unresolved_count == 0 | ||
| ) |
There was a problem hiding this comment.
Require unscoped FULL before proving impact complete
When a worklist is backed by a scoped snapshot, this can promote the raw FULL marker to impact_completeness.status == "complete" even though the graph is not exhaustive: capture_edge_snapshot(..., scope_locators=...) only queries the scoped entities and still writes completeness = "FULL" when there are no failures, and reverify's lazy capture passes scoped seed locators for cold stores. In that cold/scoped path an attest bundle can then make risk_verification.risk read proven while downstream entities outside the scoped capture were never considered; carry an unscoped-full signal or mark scoped captures partial before allowing complete.
Useful? React with 👍 / 👎.
| records = payload.get("records", []) | ||
| if not isinstance(records, list): | ||
| return [] | ||
| return [r for r in records if isinstance(r, dict)] |
There was a problem hiding this comment.
Reject governance records for a different SEI
If legis governance-read returns a checked envelope for a different top-level sei, or records whose sei does not match the requested one, this returns those records and _consult_legis attaches them to the current locator. In a misrouted or stale producer response, that makes an entity show governance present based on another entity's clearance; fail closed unless the envelope and each returned record are keyed to the requested sei.
Useful? React with 👍 / 👎.
| "verified_at": verified_at, | ||
| "actor": actor, | ||
| "source": "warpline", | ||
| "idempotency": "recorded" if inserted else "already_recorded", |
There was a problem hiding this comment.
Return stored metadata for idempotent verification records
When the same (repo, commit, kind) is recorded again with a different actor or at a later now, the insert is ignored by the unique key but this response is still built from the replayed arguments. The caller receives idempotency: "already_recorded" alongside a verified_at/actor that were never persisted, so audit/logging clients can believe the gate pass happened at the wrong time or by the wrong actor; fetch and report the existing row on idempotent replays.
Useful? React with 👍 / 👎.
| on_disk = max(int(meta_row["value"]), user_version) | ||
| if on_disk > HIGHEST_KNOWN_VERSION: |
There was a problem hiding this comment.
Verify schema objects before reporting binding ok
For a store whose markers claim schema v4 but whose verification_events table is missing (for example after a failed or foreign migration), this trusts the marker as long as it is not ahead of the binary and later returns binding_ok: true because the probe only queries change_events and edge_snapshots. Since reverify and verify-record now require verification_events, project_status can say this build can serve a store that will fail on the new verification paths; use the existing schema-object floor/table checks before returning ok.
Useful? React with 👍 / 👎.
| if isinstance(b, dict): | ||
| sei = b.get("sei") | ||
| if isinstance(sei, str) and sei: | ||
| by_sei[sei] = b |
There was a problem hiding this comment.
Reject duplicate attestation boundaries
Because this map silently overwrites earlier boundaries with the same SEI, a pushed attest bundle can include a non-clean or stale boundary for an affected entity followed by a clean, body-matching duplicate and still get risk: "proven". Since the bundle is explicitly untrusted and its signature is not verified here, duplicate SEI boundaries should fail closed (or require every duplicate to match cleanly) instead of letting array order mask a defect.
Useful? React with 👍 / 👎.
Release 1.2.0 line →
mainConsolidates the
release/1.2.0branch (all local work is merged here) for review intomain. 35 commits; package version1.2.0.Headline work
project_statusread-only store-binding probe (warpline.project_status.v1) — this branch's newest addition. A read-only MCP tool (warpline_project_status_get/project_status) that reports whether THIS build can read and serve the snapshot store for a repo (data.binding_ok), readingschema_versionfrom inside the store — never directory existence — so a stale-but-running warpline that can't read its.weft/warplinestore at a compatible schema is caught (the loomweave-incident failure class). It is the first genuinely read-only tool (writes_local_state=false,mutates_paths=[]): an absent store reports absent without creating a DB,warpline.dbis left byte-for-byte unchanged, and the not-bound cases (store_absent/store_unreadable/schema_ahead) all givebinding_ok=false+schema_version=null.impact_completenessself-assessment onreverify_worklist.v1; the Rung-2 wardline-attest-2 risk-as-verification consumer; and the capability-gated legisgovernance_read.v1consumer wired intoreverify --include_federation.Lacuna harness handoff (project_status)
warpline wraps every tool in
CORE_OUTPUT_SCHEMA, so the probe reads facts underdata:structuredContent.data.binding_ok— the verdictstructuredContent.data.store.schema_version— assertis not null(the non-tautological store-read signal)data.resolved_root,data.store_status(ok|store_absent|store_unreadable|schema_ahead),warnings[0]How it was verified
mypy --strictclean,ruffclean,wardline scan . --fail-on ERRORexit 0..mcp.jsonuv-tool binary was rebuilt and confirmed to expose + serve the new tool live (binding_ok=true, schema_version=4).🤖 Generated with Claude Code